
// ToolsLib Project

/* ToolsLib library for RusRoute firewall and other projects of
 * Andrey A. Moiseenko / IE Moiseenko A.A. (Russia)
 * e-mail: support@maasoftware.ru, maa2002@mail.ru
 * web: http://maasoftware.ru, http://maasoftware.com, http://maasoft.ru, http://maasoft.org
 * Author's full name: Andrey Alekseevitch Moiseenko
 * (russian name: Моисеенко Андрей Алексеевич)
 */

// ToolsLib/Timer.cpp

/* Copyright (C) 2002-2024 Andrey A. Moiseenko (support@maasoftware.ru)
 * All rights reserved.
 *
 * This library contains cross-platform high resolution time calculations.
 * First dated 30.10.2009.
 * The library implementation written
 * by Andrey A. Moiseenko (support@maasoftware.ru).
 * This library and applications are
 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
 * as long as the following conditions are aheared to.
 *
 * Copyright remains Andrey A. Moiseenko, and as such any Copyright notices in
 * the code are not to be removed.  If this code is used in a product,
 * Andrey A. Moiseenko should be given attribution as the author of the parts used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Andrey A. Moiseenko (support@maasoftware.ru)
 *
 * THIS SOFTWARE IS PROVIDED BY ANDREY A. MOISEENKO ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#include "perm.h"
#include "temp.h"

#ifdef _WIN32

ULONG MaaAdd128(ULARGE_INTEGER *x, const ULARGE_INTEGER * y) noexcept
{
    ULARGE_INTEGER tmp;
    tmp.QuadPart = (unsigned long long)x[0].LowPart + (unsigned long long)y[0].LowPart;
    x[0].LowPart = tmp.LowPart;
    tmp.LowPart = tmp.HighPart;
    tmp.HighPart = 0;

    tmp.QuadPart += (unsigned long long)x[0].HighPart + (unsigned long long)y[0].HighPart;
    x[0].HighPart = tmp.LowPart;
    tmp.LowPart = tmp.HighPart;
    tmp.HighPart = 0;

    tmp.QuadPart += (unsigned long long)x[1].LowPart + (unsigned long long)y[1].LowPart;
    x[1].LowPart = tmp.LowPart;
    tmp.LowPart = tmp.HighPart;
    tmp.HighPart = 0;

    tmp.QuadPart += (unsigned long long)x[1].HighPart + (unsigned long long)y[1].HighPart;
    x[1].HighPart = tmp.LowPart;
    tmp.LowPart = tmp.HighPart;
    tmp.HighPart = 0;

    return tmp.LowPart;
}

void MaaMul64x64(const ULARGE_INTEGER &xx, const ULARGE_INTEGER &yy, ULARGE_INTEGER *res) noexcept
{
#ifdef _AMD64_
    res[0].QuadPart = _umul128(xx.QuadPart, yy.QuadPart, &res[1].QuadPart);
#else
    ULARGE_INTEGER nn[4][2];

    //(10a + b)*(10c + d) = (100ac + 10*(bc + ad) + bd);
    nn[0][0].QuadPart = 0;
    nn[0][1].QuadPart = (unsigned long long)xx.HighPart * (unsigned long long)yy.HighPart;

    nn[1][0].QuadPart = (unsigned long long)xx.LowPart * (unsigned long long)yy.LowPart;
    nn[1][1].QuadPart = 0;

    nn[2][0].QuadPart = (unsigned long long)xx.LowPart * (unsigned long long)yy.HighPart;
    nn[2][1].LowPart = nn[2][0].HighPart;
    nn[2][1].HighPart = 0;
    nn[2][0].HighPart = nn[2][0].LowPart;
    nn[2][0].LowPart = 0;

    nn[3][0].QuadPart = (unsigned long long)xx.HighPart * (unsigned long long)yy.LowPart;
    nn[3][1].LowPart = nn[3][0].HighPart;
    nn[3][1].HighPart = 0;
    nn[3][0].HighPart = nn[3][0].LowPart;
    nn[3][0].LowPart = 0;

    MaaAdd128(nn[0], nn[1]);
    MaaAdd128(nn[0], nn[2]);
    MaaAdd128(nn[0], nn[3]);
    res[0].QuadPart = nn[0][0].QuadPart;
    res[1].QuadPart = nn[0][1].QuadPart;
#endif
}

CMaaTime::CMaaTime(CMaaTime &That) noexcept
{
    m_LastTimeFactor = 0;
    m_LastUsTime = 0;
    m_Freq = That.m_Freq;
    m_Accuraciry = That.m_Accuraciry;
    m_IAccuraciry = That.m_IAccuraciry;
    //m_NextTime__rdtsc = That.m_NextTime__rdtsc;

    m_StartTime__rdtsc = m_LastTime__rdtsc = GetCounter();
    //StartWatch();
}

CMaaTime::CMaaTime(int CalibrateMs) noexcept
{
    m_LastTimeFactor = 0;
#ifndef _IA64_
    LARGE_INTEGER tmp, tmp2, tmpf;
#ifdef __KERNEL
    KeQueryPerformanceCounter(&tmpf);
#else
    QueryPerformanceFrequency(&tmpf);
#endif
#if defined (_M_ARM) || defined (_M_ARM64)
    m_StartTime__rdtsc = m_LastTime__rdtsc = /*m_NextTime__rdtsc =*/ GetCounter();
#else
    m_StartTime__rdtsc = m_LastTime__rdtsc = /*m_NextTime__rdtsc =*/ __rdtsc();
#endif
    m_LastUsTime = 0;
    m_Accuraciry = 1;

#ifdef __KERNEL
    m_IAccuraciry = KeQueryTimeIncrement();
#else
    m_IAccuraciry = 1;
#endif

#ifdef __KERNEL
    tmp.QuadPart = KeQueryPerformanceCounter(nullptr).QuadPart;
    _uqword t = __rdtsc();
    NdisMSleep(CalibrateMs * 1000);
    tmp2.QuadPart = KeQueryPerformanceCounter(nullptr).QuadPart;
    _uqword t2 = __rdtsc();
#else
    QueryPerformanceCounter(&tmp);
    const _uqword t = GetCounter();  // __rdtsc();
    Sleep(CalibrateMs);
    QueryPerformanceCounter(&tmp2);
    const _uqword t2 = GetCounter();  // __rdtsc();
#endif
    m_Freq = MaaMulDiv64(t2 - t, tmpf.QuadPart, tmp2.QuadPart - tmp.QuadPart);

#if defined (_M_ARM) || defined (_M_ARM64)
    m_StartTime__rdtsc = m_LastTime__rdtsc = GetCounter();
#else
    m_StartTime__rdtsc = m_LastTime__rdtsc = __rdtsc();
#endif
    m_LastUsTime = 0;

#else
    // _IA64_

#ifdef __KERNEL
    LARGE_INTEGER tmpf;
    KeQueryPerformanceCounter(&tmpf);
    m_Freq = tmpf.QuadPart;
    m_Accuraciry = m_IAccuraciry = KeQueryTimeIncrement();
    m_StartTime__rdtsc = m_LastTime__rdtsc = KeQueryInterruptTime();
    m_LastUsTime = 0;
#else
    // not implemented
#endif
#endif
}

_uqword CMaaTime::GetTime(_uqword Factor) noexcept
{
    /*
    std::atomic<int> x{0};
    int x0 = x;
    while (!x.compare_exchange_strong(x0, x0 + 1)) {}
    */

    _uqword x;
    while(1)
    {
        x = GetCounter();
        _uqword y = m_LastTime__rdtsc.load();
        if  ((_qword)(x - y) < 0)
        {
            x = y;
            break;
        }
        else
        {
            if  (m_LastTime__rdtsc.compare_exchange_weak(y, x))
            {
                break;
            }
        }
    }
#if defined(__KERNEL) && defined(_IA64_)
    x = MaaMulDiv64(x - m_StartTime__rdtsc, Factor, 10000000);
#else
    x = MaaMulDiv64(x - m_StartTime__rdtsc, Factor, m_Freq);
#endif

    m_LastUsTime.store(x);
    return x;
}
_uqword CMaaTime::GetDeltaTime(_uqword t, _uqword Factor) const noexcept
{
#if defined(__KERNEL) && defined(_IA64_)
    return MaaMulDiv64(t, 10000000, Factor);
#else
    return MaaMulDiv64(t, m_Freq, Factor);
#endif
}

_uqword CMaaTime::StartWatch() noexcept
{
    const _uqword x = GetCounter();
    m_LastTimeFactor = 0;
    if  ((_qword)(x - m_LastTime__rdtsc) >= 0)
    {
        m_StartTime__rdtsc = m_LastTime__rdtsc = x;
    }
    else
    {
        m_StartTime__rdtsc = m_LastTime__rdtsc.load();
    }
    m_LastUsTime = 0;
    return 0;
}

_uqword CMaaTime::GetTimeEx(_uqword Factor) noexcept
{
    // more fast call by using time caching
    if  (Factor != m_LastTimeFactor || (GetCounter() - m_NextTime__rdtsc) >= 0)
    {
        m_LastTime = GetTime(m_LastTimeFactor = Factor);
#if defined(__KERNEL) && defined(_IA64_)
        m_NextTime__rdtsc = m_StartTime__rdtsc + MaaMulDiv64(m_LastTime + 1, 10000000, Factor);
#else
        m_NextTime__rdtsc = m_StartTime__rdtsc + MaaMulDiv64(m_LastTime + 1, m_Freq, Factor);
#endif
    }
    return m_LastTime;
}


#else
//unix
CMaaTime::CMaaTime(int /*CalibrateMs*/) noexcept
{
    StartWatch();
}
CMaaTime::CMaaTime(CMaaTime & /*That*/) noexcept
{
    StartWatch();
}
_uqword CMaaTime::StartWatch() noexcept
{
    gettimeofday(&m_StartTime, nullptr);
    return 0;
}
_uqword CMaaTime::GetTime(_uqword Factor) noexcept
{
    timeval tv;
    gettimeofday(&tv, nullptr);
    //int s1 = (tv.tv_usec - m_StartTime.tv_usec) / 1000000 - 1;
    //_qword x = (_qword)(tv.tv_sec - m_StartTime.tv_sec + s1) * Factor + (_qword)(tv.tv_usec - m_StartTime.tv_usec - s1 * 1000000) * Factor / 1000000;
    _uqword x = (_uqword)(tv.tv_sec - m_StartTime.tv_sec - 1) * Factor + (_uqword)(1000000 + tv.tv_usec - m_StartTime.tv_usec) * Factor / 1000000;
    return x;
}
_uqword CMaaTime::GetDeltaTime(_uqword t, _uqword Factor) const noexcept
{
#ifdef MaaMulDiv64
    return MaaMulDiv64(t, 1000000, Factor);
#else
    return t * (1000000 / Factor);
#endif
}
#endif // _WIN32

/*
static void constr_destr(int bCreate, void* ptr)
{
     if   (bCreate)
     {
          *(CMaaTime **)ptr = TL_NEW_ CMaaTime;
     }
     else
     {
          delete *(CMaaTime **)ptr;
          *(CMaaTime **)ptr = nullptr;
     }
}

typedef void (* __tpfn_)(int bCreate, void * param);
*/

//static __tpfn_ tmp;

//Lock_Function(sfnInitLock, CMaaLiteMutex, constr_destr, (void *)&phrt)

#ifndef TOOLSLIB_SINGLE_THREAD
#endif
#if 000
CMaaLiteMutex* __GLock__sfnInitLock(bool bInit = false) noexcept;
CMaaLiteMutex* __GLock__sfnInitLock(bool bInit) noexcept
{
    static CMaaLiteMutex* gptr = nullptr;
    if  (bInit && !gptr)
    {
        gptr = TL_NEW_ CMaaLiteMutex;
        gptr->AddRef();
        //phrt = TL_NEW_ CMaaTime;
    }
    return gptr;
}
#endif

//#define Lock_Function(name, T, constr_destr, param)
/*
int sfnInitLock(bool bLock) 
{ 
     static CMaaLiteMutex *gptr = nullptr; 
     static AutoDeref<CMaaLiteMutex> tmp(&gptr, true, constr_destr, (void *)&phrt); 
     typedef void (* tpfn_)(int bCreate, void *); 
     static tpfn_ pfn_ = constr_destr; 
     static void * (void *)&phrt_ = (void *)&phrt; 
     if   (bLock) 
     { 
          if   (!gptr) 
          { 
               gptr = TL_NEW_ CMaaLiteMutex; 
               if   (gptr && pfn_) 
               { 
                    pfn_(1, (void *)&phrt_); 
               } 
          } 
          gptr->Lock(); return 1; 
     } 
     else 
     { 
          int n = gptr->UnLock(); 
          if   (n < 0) 
          { 
               gptr = nullptr; pfn_(0, (void *)&phrt_);
          } 
          if   (n == 0 && tmp.IsDestoyed()) 
          { 
               delete gptr; 
               gptr = nullptr;  pfn_(0, (void *)&phrt_);
          } 
          return gptr ? 1 : -1; 
     } 
     return 0; 
}
*/

#if 000
CMaaTime & GetHRTime() noexcept
{
    //sfnInitLock(true);
    //sfnInitLock(false);
#ifndef TOOLSLIB_SINGLE_THREAD
    __GLock__sfnInitLock(true)->Lock();
#endif
    if (!phrt)
    {
        phrt = TL_NEW_ CMaaTime;
    }
#ifndef TOOLSLIB_SINGLE_THREAD
    __GLock__sfnInitLock()->UnLock();
#endif

    static CMaaTime * phrt2 = nullptr;
    return phrt ? *phrt : phrt2 ? *phrt2 : *(phrt2 = TL_NEW_ CMaaTime);
}
#endif

_uqword CMaaTimeStamp() noexcept // time(nullptr) * 1000000 + usec
{
#ifdef __unix__
    timeval tv;
    gettimeofday(&tv, nullptr);
    return (_uqword)tv.tv_sec * 1000000 + tv.tv_usec;
#else
    SYSTEMTIME st;
    GetSystemTime(&st);
    FILETIME ft;
    SystemTimeToFileTime(&st, &ft);
    time_t t;
    int usec;
    FileTimeToTimetAndUsec(&ft, &t, &usec);
    return (_uqword)t * 1000000 + usec;
#endif
}

time_t CMaaTimeStampToTime(_uqword ts, int * pusec) noexcept
{
    if  (pusec)
    {
        *pusec = ts % 1000000;
    }
    return (time_t) ts / 1000000;
}

/*
template <class Key> _dword CMaaHashFunction(const Key & k)
{
     return CMaaStandardHashFunction(&k, (int)sizeof(k));
}
*/
template <> _dword CMaaHashFunction<int>(const int &k) noexcept
{
    return (_dword)k;
}
#ifndef __unix__
template <> _dword CMaaHashFunction<_dword>(const _dword &k) noexcept
{
    return (_dword)k;
}
#endif
template <> _dword CMaaHashFunction<unsigned>(const unsigned &k) noexcept
{
    return (_dword)k;
}

template <> _dword CMaaHashFunction2<int>(const int k) noexcept
{
    return (_dword)k;
}
#ifndef __unix__
template <> _dword CMaaHashFunction2<_dword>(const _dword k) noexcept
{
    return (_dword)k;
}
#endif
template <> _dword CMaaHashFunction2<unsigned>(const unsigned k) noexcept
{
    return (_dword)k;
}
template <> _dword CMaaHashFunction2<_word>(const _word k) noexcept
{
    return (_dword)k;
}
template <> _dword CMaaHashFunction2<_qword>(const _qword k) noexcept
{
    return ((_dword)k) ^ (_dword)(k >> 32);
}
template <> _dword CMaaHashFunction2<_uqword>(const _uqword k) noexcept
{
    return ((_dword)k) ^ (_dword)(k >> 32);
}

time_t CMaaGetTimeOfDay(int* pMicroseconds) noexcept
{
    if  (!pMicroseconds)
    {
        return time(NULL);
    }
#ifdef _WIN32
    time_t t;
    while(1)
    {
        SYSTEMTIME st1, st2;
        memset(&st1, 0, sizeof(st1));
        memset(&st2, 0, sizeof(st2));
        t = time(nullptr);
        GetSystemTime(&st1);
        time_t t2 = time(nullptr);
        GetSystemTime(&st2);
        if  (t2 == t && st2.wMilliseconds >= st1.wMilliseconds)
        {
            *pMicroseconds = st2.wMilliseconds * 1000;
            break;
        }
    }
    return t;
    /*
    SYSTEMTIME Time;
    memset(&Time, 0, sizeof(Time));
    tm tt;
    memset(&tt, 0, sizeof(tt));
    GetLocalTime(&Time);
    *pMilliseconds = Time.wMilliseconds;
    tt.tm_hour = Time.wHour;
    tt.tm_min = Time.wMinute;
    tt.tm_sec = Time.wSecond;
    tt.tm_mday = Time.wDay;
    tt.tm_mon = Time.wMonth - 1;
    tt.tm_year = Time.wYear - 1900;
    tt.tm_isdst = -1;
    return mktime(&tt);
    */
#else
    timeval tv;
    gettimeofday(&tv, nullptr);
    *pMicroseconds = tv.tv_usec;
    return tv.tv_sec;
#endif
}

/*
int main()
{
     LARGE_INTEGER tmp;
     QueryPerformanceFrequency(&tmp);
     _uqword q = tmp.QuadPart;
     __utf8_printf("q = %,D (0x%08x%08x)\n", q, (_dword)(q >> 32), (_dword)(q & 0xffffffff));
     return 0;
}
// C2Q 9550 2.83 GHz :
q = 2,764,179 (0x00000000002a2d93)
*/

/*
#ifdef _WIN32
int test_delta()
{
    CMaaTime& h = GetHRTime();
    DWORD t0 = GetTickCount();
    _uqword x00;
    //_uqword x0 = (x00 = h.GetCounter()) + h.GetDeltaTime(5000000, 1000000);
    //_uqword x0 = (x00 = h.GetCounter()) + h.GetDeltaTime(5000, 1000);
    _uqword x0 = (x00 = h.GetCounter()) + h.GetDeltaTime(5, 1);
    __utf8_printf("x00=%,D, x0=%,D, x0 - x00=%,D\n", (_uqword)x00, (_uqword)x0, (_uqword)(x0 - x00));
    while (h.GetCounter() < x0);
    DWORD t = GetTickCount();
    __utf8_printf("t - t0 = 5s = %d ms\n", (int)(t - t0));
    return 0;
}
#else
int test_delta()
{
    CMaaTime& h = GetHRTime();
    DWORD t0 = GetTickCount();
    _uqword x00;
    //_uqword x0 = (x00 = h.GetCounter()) + h.GetDeltaTime(5000000, 1000000);
    //_uqword x0 = (x00 = h.GetCounter()) + h.GetDeltaTime(5000, 1000);
    _uqword x0 = (x00 = h.GetCounter()) + h.GetDeltaTime(5, 1);
    __utf8_printf("x00=%,D, x0=%,D, x0 - x00=%,D\n", x00, x0, x0 - x00);
    while (h.GetCounter() < x0);
    DWORD t = GetTickCount();
    __utf8_printf("t - t0 = 5s = %d ms\n", (int)(t - t0));
    return 0;
}
#endif
*/

#ifdef _WIN32
#ifdef MaaMulDiv64
#undef MaaMulDiv64
#endif
_uqword MaaMulDiv64(_uqword x, _uqword y, _uqword z, _uqword* pRest /*= nullptr*/, _uqword* pHighPart /*= nullptr*/) noexcept
{
    if  (pHighPart)
    {
        *pHighPart = 0;
    }
    if  (pRest)
    {
        *pRest = 0;
    }

    if  (z == 0)
    {
        return 0;
    }

    ULARGE_INTEGER xx, yy, zz, m[2];
    xx.QuadPart = x;
    yy.QuadPart = y;
    zz.QuadPart = z;
    MaaMul64x64(xx, yy, m);

    if  (m[1].QuadPart == 0)
    {
        if  (pRest)
        {
            *pRest = (_uqword)(m[0].QuadPart % zz.QuadPart);
        }
        return (_uqword)(m[0].QuadPart / zz.QuadPart);
    }

    ULARGE_INTEGER res[2];
    res[0].QuadPart = 0;
    res[1].QuadPart = 0;
    while(1)
    {
        if  (m[1].QuadPart == 0)
        {
            ULARGE_INTEGER tmp[2];
            tmp[0].QuadPart = m[0].QuadPart / zz.QuadPart;
            m[0].QuadPart %= zz.QuadPart;
            tmp[1].QuadPart = 0;
            MaaAdd128(res, tmp);
            break;
        }
        int sn;
        for (sn = 0; sn < 64; sn++)
        {
            ULARGE_INTEGER tmp;
            tmp.QuadPart = (((ULONGLONG)1) << (63 - sn));
            if  (m[1].QuadPart & tmp.QuadPart)
            {
                break;
            }
        }
        ULARGE_INTEGER Dividend[2];
        Dividend[1].QuadPart = (m[1].QuadPart << sn) | (m[0].QuadPart >> (64 - sn));
        Dividend[0].QuadPart = (m[0].QuadPart << sn);

        int sz;
        for (sz = 0; sz < 64; sz++)
        {
            ULARGE_INTEGER tmp;
            tmp.QuadPart = (((ULONGLONG)1) << (63 - sz));
            if  (zz.QuadPart & tmp.QuadPart)
            {
                break;
            }
        }
        sz = 64 - sz;
        int s = 32 - sz;
        ULARGE_INTEGER Divider1;
        if  (s >= 0)
        {
            Divider1.QuadPart = zz.QuadPart << s;
        }
        else
        {
            Divider1.QuadPart = zz.QuadPart >> (-s);
        }
        ULARGE_INTEGER alpha;
        alpha.QuadPart = (Dividend[1].QuadPart - 1) / (Divider1.QuadPart + 1);
        int sh = s + 64 - sn;
        if  (sh < 0)
        {
            alpha.QuadPart >>= (-sh);
            sh = 0;
        }
        if  (alpha.QuadPart == 0)
        {
            while(m[1].QuadPart != 0 || m[0].QuadPart >= zz.QuadPart)
            {
                if  (m[0].QuadPart < zz.QuadPart)
                {
                    m[1].QuadPart--;
                }
                m[0].QuadPart -= zz.QuadPart;
                if  ((++res[0].QuadPart) == 0)
                {
                    res[1].QuadPart++;
                }
            }
            break;
        }
        ULARGE_INTEGER m2[2];
        MaaMul64x64(alpha, zz, m2);
        m2[1].QuadPart = (m2[1].QuadPart << sh) | (m2[0].QuadPart >> (64 - sh));
        m2[0].QuadPart <<= sh;
        if  (m[0].QuadPart < m2[0].QuadPart)
        {
            m[1].QuadPart--;
        }
        m[0].QuadPart -= m2[0].QuadPart;
        m[1].QuadPart -= m2[1].QuadPart;

        m2[1].QuadPart = alpha.QuadPart >> (64 - sh);
        m2[0].QuadPart = alpha.QuadPart << sh;
        MaaAdd128(res, m2);
    }
    if  (pHighPart)
    {
        *pHighPart = m[1].QuadPart;
    }
    if  (pRest)
    {
        *pRest = m[0].QuadPart;
    }
    return (_uqword)res[0].QuadPart;
}
#endif

#ifdef __unix__
_dword GetTickCount()
{
    timeval tv;
    gettimeofday(&tv, nullptr);
    return (_dword)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
}
#endif
